#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "merge.h"
#include "termino_merge.h"
#include "cola.h"
#include "abb_avl.h"
#include "heap.h"
#include "utilidades.h"

#define MAXLINE 1000
#define CHAR_TITLE '*'
#define TITLE_LENGTH 20
#define TRUEFLAG 't'
#define FALSEFLAG 'f'

//Imprime el nombre del test y su resultado y cambia el valor del
//caracter recibido por parámetro si la prueba dio error.
void print_test(char* name, bool resultado, char* result){

	printf("\t%s: %s\n", name, resultado? "OK" : "ERROR");
	
	if(!resultado) *result=FALSEFLAG;
	
}

//Imprime el encabezado de un menu cuyo título recibe por parámetro.
void print_menu_header(char* title){

	char* character=malloc((1+TITLE_LENGTH)*sizeof(char));

	int i;

	for(i=0;i<TITLE_LENGTH;i++) character[i]=CHAR_TITLE;

	character[i]='\0';
	
	printf("\n%s %s %s\n\n",character,title,character);

	free(character);
	
}

//Imprime el pie de un menu cuyo título recibe por parámetro.
void print_menu_footer(char* title){

	char* spaces=malloc((1+strlen(title))*sizeof(char));

	int i;

	for(i=0;i<strlen(title);i++) spaces[i]=' ';

	spaces[i]='\0';

	print_menu_header(spaces);

	free(spaces);
	
}

//Imprime el resultado de las pruebas con un mensaje característico.
void print_resultado(char* result){
	
	if(*result==TRUEFLAG){
		
		printf("\n**********");
		
		printf("El programa ha pasado todas las pruebas con éxito");
		
		printf("**********\n");
	
	}else if(*result==FALSEFLAG){
		
		printf("\nLas pruebas han encontrado errores en el programa. ");
	
		printf("Verificar su funcionamiento.\n");

	}else printf("\nError en las pruebas.\n");
	
}
/*
// Prueba el funcionamiento de la creación de la lista de colas.
void prueba_lista_colas(char* result){
		
	print_menu_header("Lista de colas");
	
	// Setup
	int numero_de_colas = 5;
	cola_t** lista_de_colas = crear_lista_de_colas(numero_de_colas);
	int i,res;
	res = 1;

	// Act
	for( i=0 ; i<numero_de_colas ; i++ )
	
		if(!lista_de_colas[i]) res = 0;
	
	// Assert
	print_test("Cantidad creada correcta",res,result);
	
	// Clean
	destruir_lista_de_colas(lista_de_colas,numero_de_colas);
	
	print_menu_footer("Lista de colas");
	
}

// Prueba el funcionamiento básico del TAD termino_merge_t.
void prueba_termino(char* result){
	
	print_menu_header("Terminos");
	
	char* str_termino = "test";
	char* str_doc1 = "testdoc1";
	char* str_doc2 = "testdoc2";
	int aparicionesdoc1 = 10;
	int aparicionesdoc2 = 20;
	termino_merge_t* nuevo_termino = termino_merge_crear(str_termino);
	
	int cota_tamanio = 3*sizeof(void*) + strlen(str_termino) + sizeof(char);
	print_test("Tamanio correcto",termino_merge_ver_tamanio(nuevo_termino) >= cota_tamanio, result);
	
	termino_merge_agregar_apariciones(nuevo_termino,str_doc1,aparicionesdoc1);
	print_test("Valor asociado al documento 1 es correcto",termino_merge_obtener_apariciones_en(nuevo_termino,str_doc1)==aparicionesdoc1,result);
	
	termino_merge_agregar_apariciones(nuevo_termino,str_doc2,aparicionesdoc2);
	print_test("Valor asociado al documento 2 es correcto",termino_merge_obtener_apariciones_en(nuevo_termino,str_doc2)==aparicionesdoc2,result);
	
	termino_merge_agregar_apariciones(nuevo_termino,str_doc1,aparicionesdoc2);
	print_test("Valor asociado al documento 1 es correcto",termino_merge_obtener_apariciones_en(nuevo_termino,str_doc1)==(aparicionesdoc1+aparicionesdoc2),result);
	
	termino_merge_destruir(nuevo_termino);
	
	print_menu_footer("Terminos");
	
}

// Prueba la combinación de términos asociados con el mismo string.
void prueba_combinar_terminos(char* result){
	
	print_menu_header("Combinar terminos");
	
	char* str_termino = "test";
	char* str_doc1 = "testdoc1";
	char* str_doc2 = "testdoc2";
	int aparicionesdoc1 = 10;
	int aparicionesdoc2 = 20;
	termino_merge_t* t1 = termino_merge_crear(str_termino);
	termino_merge_t* t2 = termino_merge_crear(str_termino);
	
	termino_merge_agregar_apariciones(t1,str_doc1,aparicionesdoc1);
	termino_merge_agregar_apariciones(t1,str_doc2,aparicionesdoc2);
	termino_merge_agregar_apariciones(t2,str_doc1,aparicionesdoc1);
	
	combinar_terminos(t1,t2);
	
	print_test("El documento que no estaba en la fuente permanece igual",termino_merge_obtener_apariciones_en(t1,str_doc2)==aparicionesdoc2,result);
	
	print_test("El documento repetido suma sus apariciones",termino_merge_obtener_apariciones_en(t1,str_doc1)==2*aparicionesdoc1,result);
	
	termino_merge_destruir(t1);
	
	print_menu_footer("Combinar terminos");
	
}

// Prueba el funcionamiento de escritura de archivo que ocurre cuando
// se limpia el buffer.
void prueba_limpiar_buffer(char* result){
	
	print_menu_header("Limpiar buffer");
	
	char* str_archivo_aparicion_simple = "simple";
	char* str_archivo_aparicion_multiple = "multi";
	FILE* archivo_aparicion_simple = fopen(str_archivo_aparicion_simple,"w");
	FILE* archivo_aparicion_multiple = fopen(str_archivo_aparicion_multiple,"w");
	
	cola_t* buffer = cola_crear();
	int n_terminos = 5;
	int apariciones = 10;
	
	char* terminos[]={"t1","t2","t3","t4","t5"};
	char* docs[]={"d1","d2","d3","d4","d5"};
	
	termino_merge_t* actual = NULL;
	int i,j;
	bool flag_documento = true;
	bool flag_apariciones = true;
	
	// Aparicion simple.
	for( i=0 ; i<n_terminos ; i++ ){
		
		actual = termino_merge_crear(terminos[i]);
		termino_merge_agregar_apariciones(actual,docs[i],apariciones);
		cola_encolar(buffer,actual);
		
	}
	
	limpiar_buffer(buffer,archivo_aparicion_simple);
	fclose(archivo_aparicion_simple);
	archivo_aparicion_simple = fopen(str_archivo_aparicion_simple,"r");
	
	// Auxiliares para verificación en archivo.
	char* entrada = NULL;
	char* documento = NULL;
	char* str_freq = NULL;
	int freq = -1;
	
	i=0;
	
	// Lectura y verificación en archivo.
	while( !feof(archivo_aparicion_simple) ){
		
		entrada = leer_linea(archivo_aparicion_simple);
		strtok(entrada,",");
		
		while(true){

			documento = strtok(NULL,",");
			if( !documento ) break;
			str_freq = strtok(NULL,",");
			freq = (int)strtof(str_freq,NULL);
			if( strcmp(documento,docs[i++])!=0 ) flag_documento = false;
			if( freq!=apariciones ) flag_apariciones = false;
			
		}
		
		free(entrada);
		
	}
	
	print_test("Aparicion simple - Documentos correctos",flag_documento,result);
	print_test("Aparicion simple - Apariciones correctas",flag_apariciones,result);
	
	//Aparicion multiple.
	for( i=0 ; i<n_terminos ; i++ ){
		
		actual = termino_merge_crear(terminos[i]);
		for( j=0 ; j<n_terminos ; j++ ) termino_merge_agregar_apariciones(actual,docs[j],apariciones);
		cola_encolar(buffer,actual);
		
	}
	
	limpiar_buffer(buffer,archivo_aparicion_multiple);
	fclose(archivo_aparicion_multiple);
	
	archivo_aparicion_multiple = fopen(str_archivo_aparicion_multiple,"r");
	
	// Lectura y verificación en archivo.
	while( !feof(archivo_aparicion_multiple) ){
		
		entrada = leer_linea(archivo_aparicion_multiple);
		strtok(entrada,",");

		i=0;
		
		while(true){

			documento = strtok(NULL,",");
			if( !documento ) break;
			str_freq = strtok(NULL,",");
			freq = (int)strtof(str_freq,NULL);
			if( strcmp(documento,docs[i++])!=0 ) flag_documento = false;
			if( freq!=apariciones ) flag_apariciones = false;
			
		}
		
		free(entrada);
		
	}
	
	print_test("Aparicion multiple - Documentos correctos",flag_documento,result);
	print_test("Aparicion multiple - Apariciones correctas",flag_apariciones,result);
	
	cola_destruir(buffer,NULL);
	remove(str_archivo_aparicion_simple);
	remove(str_archivo_aparicion_multiple);
	fclose(archivo_aparicion_simple);
	fclose(archivo_aparicion_multiple);
	
	print_menu_footer("Limpiar buffer");
	
}

// Prueba el funcionamiento de que se agregue una nueva entrada de término
// al buffer.
void prueba_buffer_agregar_elemento(char* result){
	
	print_menu_header("Buffer agregar elemento");
	
	cola_t* buffer = cola_crear();
	termino_merge_t* term = termino_merge_crear("test1");
	termino_merge_agregar_apariciones(term,"doc1",10);
	char* entrada = (char*) malloc( 20*sizeof(char) );
	strcpy(entrada,"");
	char* str_destino = "buffer_agregar";
	FILE* destino = NULL;
	int* terminos_procesados = {0};
	
	// Tamanios que se le pasa al buffer para que limpie o solo acumule
	// entradas.
	long tamanio_limpiar = 50;
	long tamanio_encolar = 0;
	
	destino = fopen(str_destino,"w");
	buffer_agregar_elemento(buffer,term,tamanio_encolar,tamanio_limpiar,destino,terminos_procesados);
	fclose(destino);
	
	destino = fopen(str_destino,"r");
	fgets(entrada,20,destino);
	print_test("No se escribio el archivo",strlen(entrada)<5,result);
	fclose(destino);
	
	destino = fopen(str_destino,"w");
	buffer_agregar_elemento(buffer,term,tamanio_limpiar,tamanio_limpiar,destino,terminos_procesados);
	fclose(destino);
	
	destino = fopen(str_destino,"r");
	fgets(entrada,20,destino);
	print_test("Se escribio el archivo",strlen(entrada)>5,result);
	fclose(destino);
	
	free(entrada);
	remove(str_destino);
	cola_destruir(buffer,NULL);
	
	print_menu_footer("Buffer agregar elemento");
	
}

// Prueba el funcionamiento de llenar una cola a partir de un archivo.
// Verifica el largo de la cola luego de levantar datos de archivo.
void prueba_llenar_cola(char* result){
	
	print_menu_header("Llenar cola");
	
	cola_t* buffer = cola_crear();
	cola_t* cola_a_llenar = cola_crear();
	termino_merge_t* actual = NULL;
	int n_terminos = 5;
	int apariciones = 10;
	char* terminos[]={"t1","t2","t3","t4","t5"};
	char* docs[]={"d1","d2","d3","d4","d5"};
	char* str_destino = "test_file";
	FILE* destino = fopen(str_destino,"w");
	
	int i;
	for( i=0 ; i<n_terminos ; i++ ){
		
		actual = termino_merge_crear(terminos[i]);
		termino_merge_agregar_apariciones(actual,docs[i],apariciones);
		cola_encolar(buffer,actual);
		
	}
	
	limpiar_buffer(buffer,destino);
	cola_destruir(buffer,NULL);
	
	fclose(destino);
	destino = fopen(str_destino,"r");
	llenar_cola(destino,cola_a_llenar,200,0);
	fclose(destino);
	
	print_test("Cola se llena correctamente",cola_largo(cola_a_llenar)==n_terminos,result);
	
	while(!cola_esta_vacia(cola_a_llenar)) termino_merge_destruir((termino_merge_t*)cola_desencolar(cola_a_llenar));
	cola_destruir(cola_a_llenar,NULL);
	
	remove(str_destino);
	
	print_menu_footer("Llenar cola");
	
}

// Prueba el comportamiento del heap de términos. En particular,
// prueba el caso en el que hay términos en el heap que comparten
// valor de string. Prueba la combinación de los mismos.
void prueba_obtener_menor_termino(char* result){
		
	print_menu_header("Obtener menor termino");
	
	char* terminos[] = {"t0","t0","t0","w1"};
	char* docs[] = {"doc1","doc2","doc1","doc1"};
	int apariciones = 10;
	int n_terminos = 4;
	termino_merge_t *t0,*t1,*t2,*t3;
	t0=t1=t2=t3=NULL;
	termino_merge_t* terminos_t[] = {t0,t1,t2,t3};
	heap_t* heap = heap_crear(comparar_claves);
	
	int i;
	
	for( i=0 ; i<n_terminos ; i++ ){
		terminos_t[i] = termino_merge_crear(terminos[i]);
		termino_merge_agregar_apariciones(terminos_t[i],docs[i],apariciones);
		heap_encolar(heap,terminos_t[i]);
	}
	
	termino_merge_t* menor = obtener_menor_termino(heap,NULL,NULL,0,0);
	abb_avl_t* avl = termino_merge_obtener_apariciones(menor);
	int* aparicionesdoc0 = (int*)(avl_obtener(avl,docs[0]));
	int* aparicionesdoc1 = (int*)(avl_obtener(avl,docs[1]));
	
	print_test("Termino menor es el correcto",strcmp(termino_merge_obtener_string(menor),terminos[0])==0,result);
	
	print_test("Cantidad de apariciones combinadas correctamente",*aparicionesdoc0==2*apariciones,result);
	
	print_test("Cantidad de apariciones combinadas correctamente",*aparicionesdoc1==apariciones,result);
	
	termino_merge_destruir(menor);
	
	menor = obtener_menor_termino(heap,NULL,NULL,0,0);
	
	print_test("Desencolo ultimo termino",strcmp(terminos[3],termino_merge_obtener_string(menor))==0,result);
	
	termino_merge_destruir(menor);
	
	heap_destruir(heap,NULL);
	
	print_menu_footer("Obtener menor termino");
	
}
*/

// Prueba visual del merge completo.
void prueba_merge(char* result){
	
	print_menu_header("Merge");
	
	FILE* dest = fopen("output-merged-file","w");
	
	int numero_de_archivos = 5;
	int i;
	char* string_i = NULL;
	
	FILE* srcs[numero_de_archivos+1];
	
	for( i=0 ; i<numero_de_archivos ; i++ ){
		string_i = (char*) malloc(20*sizeof(char));
		sprintf(string_i,"mergefile-%d",i);
		srcs[i] = fopen(string_i,"r");
		free(string_i);
	}
	
	merge_lista_archivos(srcs,numero_de_archivos,dest);
		
	print_test("Prueba visual, abrir archivo \"merged\"",true,result);
	
	fclose(dest);
	for( i=0 ; i<numero_de_archivos ; i++ ) fclose(srcs[i]);
	
	print_menu_footer("Merge");
	
}

//Ejecución de todas las pruebas.
int main(void){
	
	char* result=(char*)malloc(sizeof(char));
	
	//Si el resultado no se altera, las pruebas salieron bien.
	*result=TRUEFLAG;
	
	/*
	prueba_lista_colas(result);
	prueba_termino(result);
	prueba_combinar_terminos(result);
	prueba_limpiar_buffer(result);
	prueba_buffer_agregar_elemento(result);
	prueba_llenar_cola(result);
	prueba_obtener_menor_termino(result);
	*/

	prueba_merge(result);
	
	print_resultado(result);
	
	free(result);
	
	return 0;

}

